---
description: Row level permissions
keywords:
  - hasura
  - docs
  - permissions
  - rules
  - row level
sidebar_position: 10
---

import Thumbnail from '@site/src/components/Thumbnail';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Configure Row Permissions {#row-level-permissions}

## Introduction

Row permissions are powerful boolean expressions that help you restrict access to database rows for each database
operation and user role.

For example on a `select` operation utilizing the `X-Hasura-User-Id` session variable, you can match the user ID
in the row with the row permission: `{"id":{"_eq":"X-Hasura-User-Id"}}`

Row-level permissions can be defined using static values, values in columns (_including those in related
tables or nested objects_), session variables and operators.

Depending on the operation, the permission can be run in different ways. In the case of `select`, your permission is
used to determine whether a row can be read. In the case of `insert`, `update` and `delete`, the
boolean expression determines whether the mutation as a whole is allowed.

## Operators

In the particular permission expression example: `{"id":{"_eq":"X-Hasura-User-Id"}}` the `_eq` is the operator, and
denotes equality.

Hasura Engine has an extensive list of operators that can be used to build row-level permission expressions.
A different set of operators is available depending on the type of value which the operator is applied to, eg: string,
boolean, integer. Generally these are the same operators that you use to
[filter query results](/queries/postgres/filters/index.mdx) along with a few others.

[Check out this list](/auth/authorization/permissions/permissions-operators.mdx) for all supported permissions
operators.

## Simple row level permissions

Using the Hasura Console, you can construct permissions easily using the builder interface or manually editing the text
field. You are also able to set permissions using the Hasura CLI or API.

The following is an example of a simple permission to restrict access for `select` to rows of a products table where
the value in the `price` column is less than 1000. Let's assume this is useful for a low-level admin role.

<Tabs className="api-tabs">
<TabItem value="console" label="Console">

You can define simple `select` operation permissions using boolean expressions on the Hasura Console in **Data ->
[table] -> Permissions -> select**.

<Thumbnail
  src="/img/auth/authorization_simple-boolean-expression_2-16-1.png"
  alt="Using boolean expressions to build rules"
  width="500px"
/>

</TabItem>
<TabItem value="cli" label="CLI">

You can define permissions using boolean expressions in the `metadata -> databases -> [database-name] -> tables ->
[table-name].yaml` file, eg:

```yaml {8-10}
- table:
    schema: public
    name: products
  select_permissions:
    - role: user
      permission:
        columns: []
        filter:
          price:
          _lt: 1000
```

Apply the Metadata by running:

```bash
hasura metadata apply
```

</TabItem>
<TabItem value="api" label="API">

You can define permissions using boolean expressions when using the
[permissions Metadata API](/api-reference/metadata-api/permission.mdx). Example with a Postgres db:

```http {13-15}
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "pg_create_select_permission",
  "args": {
    "source": "<db_name>",
    "table": "products",
    "role": "user",
    "permission": {
      "columns": "*",
      "filter": {
        "price": {
          "_lt": 1000
        }
      }
    }
  }
}
```

</TabItem>
</Tabs>

You can construct more complex boolean expressions using the `_and`, `_or` and `not` logical operators:

For example, using the `_and` operator, you can construct a rule to restrict access for `select` to rows where the
value in the `price` column is less than 1000 **and** the value in the `name` column starts with "acme":

<Tabs className="api-tabs">
<TabItem value="console" label="Console">

You can define permissions using the `_and` operator on the Hasura Console in **Data -> [table] -> Permissions -> 
select** as follows:

<Thumbnail
  src="/img/auth/composite-boolean-expression_2.16.1.png"
  alt="Example of a rule with the _and operator"
  width="600px"
/>

</TabItem>
<TabItem value="cli" label="CLI">

You can define permissions using the `_and` operator in the `metadata -> databases -> [database-name] -> tables ->
[table-name].yaml` file eg:

```yaml {8-11}
- table:
    schema: public
    name: products
  select_permissions:
    - role: user
      permission:
        columns: []
        filter:
          _and:
            - price: { _lt: 1000 }
            - name: { _ilike: acme% }
```

Apply the Metadata by running:

```bash
hasura metadata apply
```

</TabItem>
<TabItem value="api" label="API">

You can define permissions using the `_and` operator when using the
[permissions Metadata API](/api-reference/metadata-api/permission.mdx). Example with a Postgres db:

```http {13-26}
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "pg_create_select_permission",
  "args": {
    "source": "<db_name>",
    "table": "products",
    "role": "user",
    "permission": {
      "columns": "*",
      "filter": {
        "$and": [
          {
            "price": {
              "_lt": 1000
            }
          },
          {
            "name": {
              "_ilike": "acme%"
            }
          }
        ]
      }
    }
  }
}
```

</TabItem>
</Tabs>

## Permissions with session variables

Session variables that have been resolved from authentication tokens by either your authentication webhook or by
Hasura Engine using the JWT configuration are available for constructing row-level permissions.

For example, to allow a `user` to access only the products which they've added, you can use the `X-Hasura-User-ID`
session variable to compare against the `added_by_user_id` column in the `products` table.

<Tabs className="api-tabs">
<TabItem value="console" label="Console">

You can use session variables in your permissions on the Hasura Console in **Data -> [table] -> Permissions -> insert / 
select / update / delete** as follows:

<Thumbnail
  src='/img/auth/session-variables-in-permissions-simple-example_2.16.1.png'
  alt='Using session variables to build rules'
  width='600px'
/>

</TabItem>
<TabItem value="cli" label="CLI">

You can use session variables in your permissions in the `metadata -> databases -> [database-name] -> tables ->
[table-name].yaml` file, eg:

```yaml {16-18}
- table:
    schema: public
    name: products
  select_permissions:
    - role: user
      permission:
        columns:
          - id
          - name
          - description
          - price
          - manufacturer
          - category
          - image
          - added_by_user_id
        filter:
          added_by_user_id:
            _eq: X-Hasura-User-Id
```

Apply the Metadata by running:

```bash
hasura metadata apply
```

</TabItem>
<TabItem value="api" label="API">

You can define session variables in permissions tables when using the
[permissions Metadata API](/api-reference/metadata-api/permission.mdx). Example using a Postgres db:

```http {13-15}
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "pg_create_select_permission",
  "args": {
    "source": "<db_name>",
    "table": "products",
    "role": "user",
    "permission": {
      "columns": "*",
      "filter": {
        "added_by_user_id": "X-Hasura-User-Id"
      }
    }
  }
}
```

</TabItem>
</Tabs>

:::info Array session variables in permission rules

Support for using session variables for array operators like `_in`, `_nin`, `_has_any_keys`, `_has_all_keys` is
available in versions `v1.0.0-beta.3` and above.

When you use array operators such as `_in` in the permissions builder in the Hasura Console, it will automatically open
an array for your values. If your session variable value is already an array, you can click the `[X-Hasura-Allowed-Ids]`
suggestion to remove the brackets and set your session variable in its place.

Here is an example of an array-based session variable:

```bash
X-Hasura-Allowed-Ids: {1,2,3}
```

And the related permission configuration:

```yaml
permission:
  filter:
    user_id:
      _in: X-Hasura-Allowed-Ids
```

:::

## Permissions with relationships or nested objects {#relationships-in-permissions}

You can leverage [table relationships](/schema/postgres/table-relationships/index.mdx) to define permission rules with
fields from a nested object.

For example, Let's say you want to check that the user making the request to a `products` table is a
member of the vendor organization which owns the product.

You have a column on the `products` table called `vendor_id` which denotes which vendor the product belongs to. You
also have an array relationship in the `products` table called `usersInVendorsByVendorId` which links to the
`users_in_vendors` table which is just a table of `user_id` and `vendor_id` which shows which users are in which
vendor.

We can use this relationship to check that the user making the request is a member of the vendor which the
product belongs to.

<Tabs className="api-tabs">
<TabItem value="console" label="Console">

You can use a nested object to build rules on the Hasura Console in **Data -> [table] -> Permissions -> insert /
select / update / delete** as follows:

<Thumbnail
  src='/img/auth/row-level-permissions_related-table-permission_2.16.png'
  alt='Using a nested object to build rules'
  width='600px'
/>

</TabItem>
<TabItem value="cli" label="CLI">

You can add permissions using relationships or nested objects in the `metadata -> databases -> [database-name] -> tables
-> [table-name].yaml` eg:

```yaml {8-11}
- table:
    schema: public
    name: products
  select_permissions:
    - role: manager
      permission:
        columns: []
        filter:
          usersInVendorsByVendorId:
            user_id:
              _eq: X-Hasura-User-Id
```

Apply the Metadata by running:

```bash
hasura metadata apply
```

</TabItem>
<TabItem value="api" label="API">

You can add permissions using relationships or nested objects when using the
[permissions Metadata API](/api-reference/metadata-api/permission.mdx). Example using a Postgres db:

```http {13-19}
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "pg_create_select_permission",
  "args": {
    "source": "<db_name>",
    "table": "products",
    "role": "manager",
    "permission": {
      "columns": "*",
      "filter": {
        "usersInVendorsByVendorId": {
          "user_id": {
            "_eq": "X-Hasura-User-Id"
          }
        }
      }
    }
  }
}
```

</TabItem>
</Tabs>

This permission rule reads as "_If the user's id is listed as a member of the vendor which owns the product then allow
access to the product_."

:::info Array and object relationships work similarly

- The above example would have worked even if the relationship were an object relationship.
- You can also check out this more elaborate
  [example](/auth/authorization/permissions/common-roles-auth-examples.mdx#nested-object-permissions-example).

:::

## Permission with remote relationships {#remote-relationships-in-permission}

Just like in-source relationships, you can also use remote source relationships to define permission rules with fields
from the remote source.

:::tip Supported from

Hasura GraphQL Engine `v2.33.0` and onwards.

:::

For example, let's say you have a Snowflake database for storing product reviews and a Postgres database for products.

You have a remote source relationship defined **from** the `reviews` table in Snowflake **to** the `products` table in
Postgres. The `products` table has a column of `owner_id`.

We can use this remote relationship to define a permission on the `products` table to make sure that the user can only
fetch the reviews that belong to them (i.e., for which the corresponding `owner_id` is equal to the `x-hasura-user-id`).

<Tabs className="api-tabs">
<TabItem value="console" label="Console">

You can use a nested object to build rules on the Hasura Console in **Data -> [table] -> Permissions -> insert /
select / update / delete** as follows:

<Thumbnail
  src='/img/auth/row-level-permission-remote-source-relationship_2.33.png'
  alt='Using a remote source relationship to build rules'
  width='600px'
/>

</TabItem>
<TabItem value="cli" label="CLI">

You can add permissions using relationships or nested objects in the `metadata -> databases -> [database-name] -> tables
-> [table-name].yaml` eg:

```yaml {8-11}
- table:
    schema: public
    name: reviews
  select_permissions:
    - role: user
      permission:
        columns: *
        filter:
          review_product:
            owner_id:
              _eq: X-Hasura-User-Id
```

Apply the Metadata by running:

```bash
hasura metadata apply
```

</TabItem>
<TabItem value="api" label="API">

You can add permissions using relationships or nested objects when using the
[permissions Metadata API](/api-reference/metadata-api/permission.mdx). Example using a Snowflake db:

```http {13-19}
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "snowflake_create_select_permission",
  "args": {
    "source": "<db_name>",
    "table": "reviews",
    "role": "user",
    "permission": {
      "columns": "*",
      "filter": {
        "review_product": {
          "owner_id": {
            "_eq": "X-Hasura-User-Id"
          }
        }
      }
    }
  }
}
```

</TabItem>
</Tabs>

:::info What does this permission rule do?

This permission rule reads as "_If the user's id corresponds to the user who owns the product, then allow
access to the review_."

:::

### Limitations with remote relationship predicates

The remote relationship predicate must follow this template:
```json
{
  "<rel name>": {
    "<remote column name>": {
      "<op>": "<some session variable name or some literal text>"
    }
  }
}
  ```
- The remote relationship predicate can only be one level deep. However, the predicate can be nested in an `_and`,
  `_or`, `_not` or `_exists` node.
- Only `_eq`, `_neq`, `_gt`, `_lt`, `_gte`, `_lte`, `_in`, `_nin`, `_like`, `_nlike` and `_is_null` operators are
  supported for comparing fields.
- Only columns from the remote source can be compared with session variables or literal text only.

- The remote source relationship permission can only be defined for Postgres and [data
connector](/databases/data-connectors/index.mdx) backends.

- The remote source relationship backend (on the right-hand side) can be **Postgres only**.

- Subscriptions on tables that use remote relationships in permissions are **not supported**.

## Permissions with unrelated tables or views {#unrelated-tables-in-permissions}

You can use the `_exists` operator to set a permission rule based on tables or views that are not related to our table.

For example, say you want to allow a user to `insert` a `product` only if the value of the `allow_product_create`
column in the `users` table is set to `true`.

In this case we'll need to check that both the user who is making the request exists in the `users` table AND that
the `allow_product_create` column is set to `true`. Let's assume the user's id is passed in the `X-Hasura-User-ID`
session variable.

<Tabs className="api-tabs">
<TabItem value="console" label="Console">

You can set permissions using unrelated tables on the Hasura Console in **Data -> [table] -> Permissions -> insert / 
select / update / delete** as follows:

<Thumbnail src='/img/auth/row-level-permissions_unrelated-table-permission_2.16.png' alt='Use an unrelated table to build rules' />

</TabItem>
<TabItem value="cli" label="CLI">

You can set permissions using unrelated tables in the `metadata -> databases -> [database-name] -> tables ->
[table-name].yaml`, eg:

```yaml {7-15}
- table:
    schema: public
    name: products
  insert_permissions:
    - role: user
      permission:
        check:
          _exists:
            _where:
              _and:
                - id: { _eq: X-Hasura-User-Id }
                - allow_product_create: { _eq: true }
            _table:
              schema: public
              name: users
        columns:
          - id
          - name
          - price
          - description
```

Apply the Metadata by running:

```bash
hasura metadata apply
```

</TabItem>
<TabItem value="api" label="API">

You can set permissions for unrelated tables when using the
[permissions Metadata API](/api-reference/metadata-api/permission.mdx). Example using a Postgres db:

```http {13-27}
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "pg_create_insert_permission",
  "args": {
    "source": "<db_name>",
    "table": "products",
    "role": "user",
    "permission": {
      "columns": "*",
      "check": {
        "$exists": {
          "_table": "users",
          "_where": {
            "$and": [
              {
                "id": "X-Hasura-User-Id"
              },
              {
                "allow_product_create": true
              }
            ]
          }
        }
      }
    }
  }
}
```

</TabItem>
</Tabs>

This permission rule reads as "_if there exists a row in the table_ `users` _whose_ `id` _is the same as the requesting
user's_ `id` _AND has the_ `allow_product_create` _column set to true, allow access to insert products_."

## Post-update check row permissions

The update database operation has both a "Pre-update check" and a "Post-update check" for its row permissions. This
is useful if your new data post update is unknown until it is changed.

For example, say that in an update mutation we are updating a number by an amount and the result is not allowed to be
negative or higher than a certain value. This would be a perfect place to use the "Post-update check" permission.

<Thumbnail src='/img/auth/permissions_post-update-check_2-17-0.png' alt='Post-update check row permission' />
